package com.zzyl.serve.service.impl;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.serve.domain.AlertData;
import com.zzyl.serve.domain.Device;
import com.zzyl.serve.domain.DeviceData;
import com.zzyl.serve.mapper.DeviceMapper;
import com.zzyl.serve.service.IalertDataService;
import com.zzyl.system.mapper.SysUserRoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.serve.mapper.AlertRuleMapper;
import com.zzyl.serve.domain.AlertRule;
import com.zzyl.serve.service.IalertRuleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

/**
 * 报警规则Service业务层处理
 * 
 * @author liufan
 * @date 2025-08-26
 */
@Service
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IalertRuleService
{
    @Autowired
    private AlertRuleMapper alertRuleMapper;
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private SysUserRoleMapper userRoleMapper;
    @Autowired
    private IalertDataService alertDataService;


    /**
     * 查询报警规则
     * 
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectalertRuleById(Long id)
    {
        return alertRuleMapper.selectById(id);
    }

    /**
     * 查询报警规则列表
     * 
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectalertRuleList(AlertRule alertRule)
    {
        return alertRuleMapper.selectalertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertalertRule(AlertRule alertRule)
    {
        return alertRuleMapper.insert(alertRule);
    }

    /**
     * 修改报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updatealertRule(AlertRule alertRule)
    {
        return alertRuleMapper.updateById(alertRule);
    }

    /**
     * 批量删除报警规则
     * 
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deletealertRuleByIds(Long[] ids)
    {
        return alertRuleMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除报警规则信息
     * 
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deletealertRuleById(Long id)
    {
        return alertRuleMapper.deleteById(id);
    }

    @Override
    public void handleAlertData() {
        //查询已启动的报警规则
        LambdaQueryWrapper<AlertRule> wrapper = Wrappers.<AlertRule>lambdaQuery()
                .eq(AlertRule::getStatus,'1');
        long count = count(wrapper);
        //如果报警规则为空直接结束方法
        if (count == 0){
            return;
        }
        //从Redis获取设备上报数据列表
        List<Object> deviceDataList = redisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
        //如果为空则直接结束
        if (ObjectUtil.isEmpty(deviceDataList)){
            return;
        }
        //解析上报的数据，转换成List<DeviceData>
        ArrayList<DeviceData> list = new ArrayList<>();
        for (Object o : deviceDataList) {
            List<DeviceData> deviceData = JSONUtil.toList(o.toString(), DeviceData.class);
            list.addAll(deviceData);
            for (DeviceData data : list) {
                handleDeviceData(data);
            }
        }
    }

    private void handleDeviceData(DeviceData data) {
        //判断设备数据上报时间已过去了1分钟？
        //使用工具计算上报时间距离现在的时间值
        //Duration between = LocalDateTimeUtil.between(data.getAlarmTime(), LocalDateTime.now());
        long between = LocalDateTimeUtil.between(data.getAlarmTime(), LocalDateTime.now(), ChronoUnit.SECONDS);
        //如果时间大于60秒则直接结束方法
        if (between > 60){
            return;
        }
        //如果没有则查询所有针对此设备的有效规则
        LambdaQueryWrapper<AlertRule> wrapper = Wrappers.<AlertRule>lambdaQuery()
                        .eq(AlertRule::getProductKey,data.getProductKey())
                        .eq(AlertRule::getFunctionId,data.getFunctionId())
                        .in(AlertRule::getIotId,'1',data.getIotId())
                        .eq(AlertRule::getStatus,'1');
        List<AlertRule> list = list(wrapper);
        //规则为空则直接结束
        if (ObjectUtil.isEmpty(list)){
            return;
        }
        for (AlertRule alertRule : list) {
            deviceDataHandle(alertRule,data);
        }
    }

    private void deviceDataHandle(AlertRule alertRule, DeviceData data) {
        //获取报警规则的生效时间范围，并对开始和结束时间进行截取
        String[] split = alertRule.getAlertEffectivePeriod().split("~");
        //转换开始和结束时间的格式为LocalDateTime
        String start = split[0];
        LocalDateTime startTime = LocalDateTime.parse(start, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String end = split[1];
        LocalDateTime endTime = LocalDateTime.parse(end, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //获取设备上报的时间
        LocalDateTime alarmTime = data.getAlarmTime();
        //判断上报时间是否在生效时间范围内，如果不在则直接结束方法
        if (alarmTime.isBefore(startTime) || alarmTime.isAfter(endTime)){
            return;
        }
        //设置redis缓存持续周期key
        String alertFlag = StringUtils.format(CacheConstants.IOT_ALERT_COUNT_KEY, data.getIotId(), alertRule.getId());
        //判断上报数据是否达到阈值，未达到直接结束方法
        if (alertRule.getOperator() .equals(">=") && Double.parseDouble(data.getDataValue()) < alertRule.getValue()
            || alertRule.getOperator() .equals("<") && Double.parseDouble(data.getDataValue()) >= alertRule.getValue()){
            //上报数据没有达到阈值，所以清除缓存数据
            redisTemplate.delete(alertFlag);
            return;
        }
        //设置redis缓存沉默周期key
        String silentFlag = StringUtils.format(CacheConstants.IOT_ALERT_SILENT_KEY,data.getIotId(),alertRule.getId());
        //如果沉默周期值存在，说明还处于沉默周期，直接结束方法
        if(ObjectUtil.isNotEmpty(redisTemplate.opsForValue().get(silentFlag))){
            return;
        }

        //不在沉默周期，所以持续周期值加1
        Long durationTime = redisTemplate.opsForValue().increment(CacheConstants.IOT_ALERT_COUNT_KEY, 1);
        //如果持续周期小于设置的持续周期则直接结束方法
        if (durationTime < alertRule.getDuration()){
            return;
        }
        //触发警报成功，删除警告出发次数
        redisTemplate.delete(alertFlag);
        //进入沉默周期(设置沉默周期标识，并设置过期时间为一个沉默周期，过期自动失效，结束沉默周期)
        redisTemplate.opsForValue().set(silentFlag,'1',alertRule.getAlertSilentPeriod(), TimeUnit.MINUTES);
        //查找上报的数据类型
        /*LambdaQueryWrapper<AlertRule> wrapper = Wrappers.<AlertRule>lambdaQuery()
                        .eq(AlertRule::getIotId,data.getIotId());
        AlertRule alertRuleOne = getOne(wrapper);*/

        //找到报警的责任人
        Set<Long> userIds = new HashSet<>();
        if (alertRule.getAlertDataType() == 0) {
            //如果是老人异常数据，则找到老人的护理员
            if (ObjectUtil.equals(data.getLocationType(),0)){
                //是随身设备的数据，设备绑定在老人身上，根据设备id直接找到老人的护理员id集合
                userIds = deviceMapper.selectNurseIdsByElderIotId(data.getIotId());
            } else if (ObjectUtil.equals(data.getLocationType(),1)
                    && ObjectUtil.equals(data.getPhysicalLocationType(),2)) {
                // 是固定设备，绑定在床位上，则要根据床位找到老人，再找到老人的护理员
                userIds = deviceMapper.selectNurseIdsByBedIotId(data.getIotId());
            }
        } else if (alertRule.getAlertDataType() == 1) {
            // 如果是设备异常数据，则找到维修人员
            userIds = userRoleMapper.selectUserIdsByRoleName("维修工");
        }
        // 超级管理员也必须能收到报警
         Set<Long> admins = userRoleMapper.selectUserIdsByRoleName("超级管理员");
         userIds.addAll(admins);

        //保存报警数据
        batchSaveAlertData(userIds,alertRule,data);
    }

    private void batchSaveAlertData(Set<Long> userIds, AlertRule alertRule, DeviceData data) {
        List<AlertData> list = new ArrayList<>();

        //准备报警原因
        String reason = alertRule.getFunctionName() + alertRule.getOperator() + alertRule.getValue()
                + ", 持续" + alertRule.getDuration() + "个周期，发出警报";

        //遍历每个报警责任人
        for (Long userId : userIds) {
            //封装报警数据
            AlertData alertData = new AlertData();
            BeanUtil.copyProperties(data,alertData);
            //将id置空，避免将deviceData的id值拷贝给alertData的id属性
            alertData.setId(null);
            alertData.setAlertRuleId(alertRule.getId());
            alertData.setAlertReason(reason);
            alertData.setType(alertRule.getAlertDataType());
            //报警数据状态，0未处理
            alertData.setStatus(0);
            alertData.setUserId(userId);

            list.add(alertData);
        }
        alertDataService.saveBatch(list);
    }
}
